import * as path from 'path'
import * as fs from 'fs'
import { defineConfig, loadEnv } from 'vite'
import Vue from '@vitejs/plugin-vue'
import VueJsx from '@vitejs/plugin-vue-jsx'
import Pages from 'vite-plugin-pages'
import Layouts from 'vite-plugin-vue-layouts'
import ViteIcons, { ViteIconsResolver } from 'vite-plugin-icons'
import ViteComponents from 'vite-plugin-components'
import Markdown from 'vite-plugin-md'
import WindiCSS from 'vite-plugin-windicss'
import { VitePWA } from 'vite-plugin-pwa'
import VueI18n from '@intlify/vite-plugin-vue-i18n'
import Prism from 'markdown-it-prism'
import { injectHtml } from 'vite-plugin-html'
import legacy from '@vitejs/plugin-legacy'
import visualizer from 'rollup-plugin-visualizer'
import cheerio from 'cheerio'

// https://vitejs.dev/config/
// noinspection JSUnusedGlobalSymbols
export default defineConfig(p => {
  console.debug('vite mode: ', p.mode)
  const {
    /**
     * use ssr (todo: not support config js file yet)
     */
    VITE_APP_SSR,
    /**
     * analyze dist bundle size
     */
    VITE_APP_ANALYZE,
    /**
     * build extra js for legacy browsers do not support 'script type="module"'
     */
    VITE_APP_LEGACY
  } = loadEnv(p.mode, '')
  const base = VITE_APP_SSR ? '/' : './'
  return {
    base,
    // https://github.com/antfu/vite-ssg
    ssgOptions: {
      includedRoutes: _ => [],
      script: 'async',
      formatting: 'minify',
    },

    optimizeDeps: {
      include: [
        'vue',
        '@vueuse/core',
        '@intlify/core-base',
      ],
    },

    plugins: [
      Vue({
        include: [/\.vue$/, /\.md$/],
      }),
      VueJsx(),

      // https://github.com/hannoeru/vite-plugin-pages
      Pages({
        extensions: ['vue', 'md', 'tsx'],
      }),

      // https://github.com/JohnCampionJr/vite-plugin-vue-layouts
      Layouts(),

      // https://github.com/antfu/vite-plugin-md
      Markdown({
        wrapperClasses: 'prose prose-sm m-auto text-left',
        headEnabled: true,
        markdownItSetup(md) {
          // https://prismjs.com/
          md.use(Prism)
        },
      }),

      // https://github.com/antfu/vite-plugin-components
      ViteComponents({
        // allow auto load markdown components under `./src/components/`
        extensions: ['vue', 'md'],

        // allow auto import and register components used in markdown
        customLoaderMatcher: id => id.endsWith('.md'),

        // auto import icons
        customComponentResolvers: [
          // https://github.com/antfu/vite-plugin-icons
          ViteIconsResolver({
            componentPrefix: '',
            // enabledCollections: ['carbon']
          }),
        ],
      }),

      // https://github.com/antfu/vite-plugin-icons
      ViteIcons(),

      // https://github.com/antfu/vite-plugin-windicss
      WindiCSS({
        safelist: 'prose prose-sm m-auto text-left',
      }),

      // https://github.com/antfu/vite-plugin-pwa
      VitePWA({
        registerType: 'autoUpdate',
        manifest: {
          name: 'Vitesse',
          short_name: 'Vitesse',
          theme_color: '#ffffff',
          icons: [
            {
              src: '/pwa-192x192.png',
              sizes: '192x192',
              type: 'image/png',
            },
            {
              src: '/pwa-512x512.png',
              sizes: '512x512',
              type: 'image/png',
            },
            {
              src: '/pwa-512x512.png',
              sizes: '512x512',
              type: 'image/png',
              purpose: 'any maskable',
            },
          ],
        },
      }),

      // https://github.com/intlify/vite-plugin-vue-i18n
      VueI18n({
        include: [path.resolve(__dirname, 'locales/**')],
      }),

      // https://github.com/anncwb/vite-plugin-html
      injectHtml({
        injectData: {
          html: indexHtml({
            mode: p.mode,
            ssrBaseHref: base
          })
        }
      }),

      // https://github.com/vitejs/vite/tree/main/packages/plugin-legacy
      VITE_APP_LEGACY ? legacy() : [],
      // https://github.com/btd/rollup-plugin-visualizer
      VITE_APP_ANALYZE ? visualizer() : []
    ],
  }
})

/**
 * @param mode default is 'production'
 * @param templateDir default is 'public'
 * @param templateFile default is 'indexTemplate.html'
 * @param ssrBaseHref default is '/'
 */
function indexHtml({
  mode = 'production',
  templateDir = 'public',
  templateFile = 'indexTemplate.html',
  ssrBaseHref = '/'
}) {
  const $ = cheerio.load(fs.readFileSync(`${templateDir}/${templateFile}`))
  const root = $.root()
  function addCacheBustingTag(e = root, attr = '') {
    const refAttr = e.attr(attr)
    if (refAttr) {
      const uri = `${templateDir}/${refAttr}`
      if (uri.includes(':')) return // absolute uri
      const fileStat = fs.statSync(uri)
      const v = fileStat.mtime.toISOString()
      const i = refAttr.indexOf('?')
      const sign = i < 0 ? '?' : '&'
      e.attr(attr, `${refAttr}${sign}v=${v}`)
    }
  }
  const head = root.find('head')
  // config
  const configSelector = 'script[data-config]'
  const configSrc = head.find(configSelector).attr('src')?.replace('.js', `.${mode}.js`) ?? ''
  if (configSrc) {
    if (fs.existsSync(`${templateDir}/${configSrc}`)) head.append(`<script src="${configSrc}"></script>`)
    else console.debug(`config.${mode}.js not exists`)
  } else {
    throw new Error(`'${configSelector}' element not found`)
  }
  // base href
  head.find('base').attr('href', ssrBaseHref)
  // link href
  head.find('link').each((i, e) => {
    addCacheBustingTag($(e), 'href')
  })
  // script src
  head.find('script').each((i, e) => {
    addCacheBustingTag($(e), 'src')
  })
  return root.html()
}
